/*****************************************************************************
*   Copyright (c) 2019 Thermo Fisher Scientific. All rights reserved.        *
*****************************************************************************/
/** @file
    @section DESCRIPTION
    Source file of UART HAL driver for NXP K64 processor.

    @author  Liu Ming (ming.liu@thermofisher.com)

    @version 1.0

    @section LICENSE
    Use of the software source code and warranty disclaimers are
    identified in the Software Agreement associated herewith.

    @section GIT_PATH
    git@cnshf-cic-sw-bitbucket.apac.thermo.com:7999/vert/mcu.git
*/
#include "FreeRTOS.h"

#include "modbus_rtu.h"
#include "bsp_uart5.h"

#define HIGH_BIT        0x80

extern _Bool g_set_user_data;


Dat_ramDb dat_ram;

static uint8_t read_data(uint16_t address, uint8_t num, uint8_t* buf);
static uint8_t write_data(uint16_t address, uint8_t num, uint8_t* buf);

static uint8_t s_sci_a0_xmt_buffer[260];

#define TDLAS_REGS_NUMS	   12
F32 tdlasData[TDLAS_REGS_NUMS]; //buffer for tadla compare
uint16_t extSerConfigLocal[4]={0};


static void set_application_mode(void)
{
    /* Application is running */
   dat_ram.write.Midway_Board.state.BootloaderMode= 2; // applicatiion
	 dat_ram.read_write_info.board_type = 0X010B; // 0X010B
	
	/* Turn off communication watchdog */
    dat_ram.read_write_info.uart_a0_comm_watchdog = 0; 
    dat_ram.read.Midway_Board.BootloadModeCmd= 0x02; //0xDEAD; // clear cmd
}

/*******************************************************************************
** Function: void set_default_cal_block(void)
**
** Description: This function will set default cal block
**
** Parameters: None
**
** Returns: None
**
** Notes: None
*******************************************************************************/
void                        set_default_cal_block(void)
{
    U8 i;
    U16* U16_ptr = (U16*)&dat_ram.read_write_cal.userCalCoefficient;
    //char* serial_number_char;
    
//        for (i=0; i<sizeof(dat_ram.read_write_cal)/2; i++)
//            U16_ptr[i] = 0x02;   //0xDEFA; /* 0xDEFA as DEFAult value */

    /* Initialize the calibration fault status */	
    dat_ram.read_write_cal.cal_fault = 0x0000;
    dat_ram.read_write_cal.amb_temp_offset = 0;
    dat_ram.read_write_cal.userCalCoefficient = USER_CAL_COEF_DEFAULT;
    dat_ram.read_write_cal.factory_offset = 0.0;
    dat_ram.read_write_cal.factory_span = 20.9;
    dat_ram.write.Midway_Board.state.fault_3 |= 0x0400;	// bit 26
}
/****************************************************************************/
void                    get_version_number(void)
{

}

/*******************************************************************************
** Function: void set_default_info_block(void)
**
** Description: This function will set the default info block
**
** Parameters: None
**
** Returns: None
**
** Notes: None
*******************************************************************************/
void                    set_default_info_block(void)
{
	U8 i;
	U16* U16_ptr = &dat_ram.read_write_info.board_type;
  U16 serial_number[8] = {'K', 'a', 't', 'i', 'e'};
	char* serial_number_char = (char*)serial_number; //(char*)dat_ram.read_write_info.serial_number_char;

    for (i=0; i<sizeof(dat_ram.read_write_info)/2; i++)
        U16_ptr[i] = 0xDEFA; /* 0xDEFA as DEFAult value */

    dat_ram.read_write_info.board_type = 0X010E; // 0X010E

    get_version_number();

    for (i=0; i<16; i++)
        serial_number_char[i] = '0'; /* serial_number_char */

    dat_ram.read_write_info.sub_type = SUB_TYPE_Oxygen_Sensor;

    dat_ram.read_write_info.uptime_days = 0;
	
    dat_ram.read_write_info.uart_a0_comm_watchdog = 0;

    dat_ram.write.Midway_Board.state.fault_3 |= 0x0100;	// bit 24
}

/*******************************************************************************
** Function: U8 verify_info_block(U8 set_fault_bits)
**
** Description: This function will verify the info block
**
** Parameters: U8 set_fault_bits : to indicate if any fault is set?
**
** Returns: error : 0 - success, 4 - out of range / invalid data
**
** Notes: None
*******************************************************************************/
U8                  verify_info_block(U8 set_fault_bits)
{
	U8 error = 0;

    if (dat_ram.read_write_info.major_fw_ver > 99)
        error = 4;
    if (dat_ram.read_write_info.minor_fw_ver > 99)
        error = 4;
    if (dat_ram.read_write_info.maint_fw_ver > 99)
        error = 4;
    if (dat_ram.read_write_info.build_fw_ver > 999)
        error = 4;

    if (dat_ram.read_write_info.uptime_days > 36500)
        error = 4; /* error if more than 100 years = 365 * 100 */
    //error |= verify_serial_number((char*)dat_ram.read_write_info.serial_number_char, sizeof(dat_ram.read_write_info.serial_number_char));

    if(dat_ram.read_write_info.board_type != 0X010E)
        error = 4;
    
    if ((error) && (set_fault_bits))
        dat_ram.write.Midway_Board.state.fault_3 |= 0x0200;	// bit 25

    return error;
}

void dat_initDatabase(void)
{
	set_default_cal_block();
	set_application_mode();
	
	if (verify_info_block(1) != 0)
	{
			set_default_info_block();
	}
	//memset(&dat_ram.read_write_cal.external_reserve[0], 0 , 80);
	
	/* LM test */
	dat_ram.write.Midway_Board.state.fault_0 = 3;
	dat_ram.write.Midway_Board.state.fault_1 = 4;
	dat_ram.write.Midway_Board.state.fault_2 = 5;
	dat_ram.write.Midway_Board.state.fault_3 = 6;
	dat_ram.write.Midway_Board.state.uptime_days = 7;
	dat_ram.write.Midway_Board.num_samples = 8;
//	dat_ram.read.Midway_Board.addr_tarUBM_temp = 170;
//  dat_ram.read.Midway_Board.addr_tarProbeFil_temp  = 160;
//	dat_ram.read.Midway_Board.addr_tarEject_temp  = 160;
//	dat_ram.read.Midway_Board.addr_tarStinger_temp = 160;
//	dat_ram.read.Midway_Board.addr_tarPB_temp = 200;
}


///****************************************************************************
//   Executes Serial interface Modbus (MB) commands and transmits reply message.
//*/
void  MB_execute_cmd(void)
{
    uint16_t length_of_reply=4,index;
    uint16_t crc=0;
    uint8_t  exception_code = 0;

    switch (MB_FUNCTION_CODE)
    {
        case READ_HOLDING_REGS_FUNCT_CODE: // 0x03  same as 0x04
        case READ_INPUT_REGS_FUNCT_CODE:   // 0x04
            if ( (FUNCT_CODE_0x04_QUANTITY_OF_REGS < 0x0001) || (FUNCT_CODE_0x04_QUANTITY_OF_REGS > 0x007D) )
            {
                exception_code = 3;
            }
            else
                exception_code = read_data(FUNCT_CODE_0x04_START_ADDRESS, FUNCT_CODE_0x04_QUANTITY_OF_REGS * 2, &s_sci_a0_xmt_buffer[3]);

            if (exception_code == 0)
            {
                s_sci_a0_xmt_buffer[0] = MB_SLAVE_ADDRESS;
                s_sci_a0_xmt_buffer[1] = MB_FUNCTION_CODE;
                s_sci_a0_xmt_buffer[2] = FUNCT_CODE_0x04_QUANTITY_OF_REGS * 2; /* 2 bytes per register */
                /* s_sci_a0_xmt_buffer[3] contains Registers value, filled by read_data() */
                length_of_reply = 3 + FUNCT_CODE_0x04_QUANTITY_OF_REGS * 2;
            }
            break;
    
        // not tested
        case WRITE_SINGLE_REG_FUNCT_CODE: // 0x06
            /* exception_code = 3 won't happen because the statement "if( 0 <= Register Value <= 0xFFFF )" is never false */
        
            exception_code = write_data(FUNCT_CODE_0x06_REG_ADDRESS, 1 * 2, &cmd_Buf[4]);
        
        
            if (exception_code == 0)
            {
                s_sci_a0_xmt_buffer[0] = MB_SLAVE_ADDRESS;
                s_sci_a0_xmt_buffer[1] = MB_FUNCTION_CODE;
                s_sci_a0_xmt_buffer[2] = cmd_Buf[2]; /* FUNCT_CODE_0x06_REG_ADDRESS high */
                s_sci_a0_xmt_buffer[3] = cmd_Buf[3]; /* FUNCT_CODE_0x06_REG_ADDRESS low */
                s_sci_a0_xmt_buffer[4] = cmd_Buf[4]; /* FUNCT_CODE_0x06_REG_VALUE high */
                s_sci_a0_xmt_buffer[5] = cmd_Buf[5]; /* FUNCT_CODE_0x06_REG_VALUE low */
                length_of_reply = 6;
            }
            break;
    
        case WRITE_MULTIPLE_REGS_FUNCT_CODE: // 0x10
            if ((FUNCT_CODE_0x10_QUANTITY_OF_REGS < 1) || (FUNCT_CODE_0x10_QUANTITY_OF_REGS > 0x007B))
            {
                exception_code = 03; /* out of range */
            }
            else if( FUNCT_CODE_0x10_BYTE_COUNT != (uint8_t)FUNCT_CODE_0x10_QUANTITY_OF_REGS * 2 )
                exception_code = 03; /* out of range */
 
        if (exception_code == 0)
                exception_code = write_data(FUNCT_CODE_0x10_START_ADDRESS, FUNCT_CODE_0x10_QUANTITY_OF_REGS * 2, &FUNCT_CODE_0x10_REGS_VALUE);
        

            if (exception_code == 0)
            {
                s_sci_a0_xmt_buffer[0] = MB_SLAVE_ADDRESS;
                s_sci_a0_xmt_buffer[1] = MB_FUNCTION_CODE;
                s_sci_a0_xmt_buffer[2] = cmd_Buf[2]; /* FUNCT_CODE_0x10_START_ADDRESS high */
                s_sci_a0_xmt_buffer[3] = cmd_Buf[3]; /* FUNCT_CODE_0x10_START_ADDRESS low */
                s_sci_a0_xmt_buffer[4] = cmd_Buf[4]; /* FUNCT_CODE_0x10_QUANTITY_OF_REGS high */
                s_sci_a0_xmt_buffer[5] = cmd_Buf[5]; /* FUNCT_CODE_0x10_QUANTITY_OF_REGS low */
                length_of_reply = 6;                 //no  + FUNCT_CODE_0x10_QUANTITY_OF_REGS * 2;
            }
            break;
    
        // not tested                
        case READ_WRITE_MULTIPLE_REGS_FUNCT_CODE: // 0x17
            /* read input register and write holding registers */
            if( (FUNCT_CODE_0x17_QUANTITY_TO_READ < 1) || (FUNCT_CODE_0x17_QUANTITY_TO_READ > 0x007D) )
                exception_code = 03;
            else if( (FUNCT_CODE_0x17_QUANTITY_TO_WRITE < 1) || (FUNCT_CODE_0x17_QUANTITY_TO_WRITE > 0x0079) )
                exception_code = 03;
            else if(FUNCT_CODE_0x17_WRITE_BYTE_COUNT != (uint8_t)FUNCT_CODE_0x17_QUANTITY_TO_WRITE * 2)
                exception_code = 03;
        
            if (exception_code == 0)
                exception_code = read_data(FUNCT_CODE_0x17_READ_START_ADDRESS, FUNCT_CODE_0x17_QUANTITY_TO_READ * 2, &s_sci_a0_xmt_buffer[3]);
            
            if (exception_code == 0)
                exception_code = write_data(FUNCT_CODE_0x17_WRITE_START_ADDRESS, FUNCT_CODE_0x17_QUANTITY_TO_WRITE * 2, &FUNCT_CODE_0x17_WRITE_REGS_VALUE);
        
            if (exception_code == 0)
            {
                s_sci_a0_xmt_buffer[0] = MB_SLAVE_ADDRESS;
                s_sci_a0_xmt_buffer[1] = MB_FUNCTION_CODE;
                s_sci_a0_xmt_buffer[2] = FUNCT_CODE_0x17_QUANTITY_TO_READ * 2;
                length_of_reply = 3 + FUNCT_CODE_0x17_QUANTITY_TO_READ * 2;
            }
            break;
    
        default:    /* Illegal Function Code. */
            s_sci_a0_xmt_buffer[0] = MB_SLAVE_ADDRESS;
            s_sci_a0_xmt_buffer[1] = MB_FUNCTION_CODE | HIGH_BIT;
            s_sci_a0_xmt_buffer[2] = 01; /* exception_code = 01 */
            length_of_reply = 3;
            break;
    }

    if (exception_code != 0)
    {
        s_sci_a0_xmt_buffer[0] = MB_SLAVE_ADDRESS;
        s_sci_a0_xmt_buffer[1] = MB_FUNCTION_CODE | HIGH_BIT;
        s_sci_a0_xmt_buffer[2] = exception_code;
        length_of_reply = 3;
    }

    index = length_of_reply;

    /* now calculate the CRC16 */
    crc = modbus_crc16(s_sci_a0_xmt_buffer, length_of_reply);

    /* swap calculated CRC code */
    s_sci_a0_xmt_buffer[index] 		 =  crc & 0x00FF; /* low byte */
    s_sci_a0_xmt_buffer[index + 1] = (crc & 0xFF00)>>8; /* high byte */
            
    /* Done executing command.  As long as no CRC error was detected, transmit
       reply mesage to host. */
   
    /* Non RS485, if use RS485, need add ENABLE Func */
   // tfUartWrite(iQ_PORT,s_sci_a0_xmt_buffer, length_of_reply + 2); /* +2 for CRC */
		Usart_SendArray(UART5,s_sci_a0_xmt_buffer, length_of_reply + 2); /* +2 for CRC */

    /******** test test test *****************************************************/
    //SET_LED_1;
//    CLEAR_LED_1;
    /*****************************************************************************/

    return;
}


/****************************************************************************
   This Function will write to registers either RAM  Non_volatile portions of
   memory. the data to be written can be a string of contiguous data.
*/
static uint8_t write_data(uint16_t address, uint8_t num, uint8_t* buf)  //*Master Write
{
	uint8_t i;
	uint16_t offset;
	uint8_t* bytes_ptr;
	uint8_t  exception_code = 0;
	bool info_block_changed = FALSE;
	bool cal_block_changed  = FALSE;
	bool input_regs_changed = FALSE;

	/* Read/write registers; Block name ModuleInfo */
	if ( (address >= INFO_REGS_ADDR_START) && (address <= INFO_REGS_ADDR_END) ) // ModuleInfo
	{
		 exception_code = 4; /* Writing to these variables is password protected */
	}

	/* write only registers; Block name Password */
	else if ( (address >= PWORD_REGS_ADDR_START) && (address <= PWORD_REGS_ADDR_END) ) //*Master write Password
	{
			address = address - PWORD_REGS_ADDR_START;
			offset = address * sizeof(uint16_t);//same as     uint8_t offset = address * 2;

			if (offset + num > sizeof(dat_ram.password))
					exception_code = 2; /* out of range */
			else
			{
					bytes_ptr = (uint8_t*)&dat_ram.password;
			}
	}
   
    /* Read/Write registers: Block name Writings */
    else if ( (address >= IN_REGS_ADDR_START) && (address <= IN_REGS_ADDR_END) ) //*Writing
    {
        address = address - IN_REGS_ADDR_START;
        offset = address * sizeof(uint16_t);//same as     uint8_t offset = address * 2;
        if (offset + num > sizeof(dat_ram.read))
            exception_code = 2; /* out of range */
        else
        {
            bytes_ptr = &dat_ram.read.bytes[0];
            //saveBLmode = dat_ram.read.dmc.BootloadModeCmd;
            input_regs_changed = TRUE; /* something changed */
        }
    }
    
    /* Read/write registers: Block name Calibrations */
    else if ( (address >= CAL_REGS_ADDR_START) && (address <= CAL_REGS_ADDR_END))
    {
        address = address - CAL_REGS_ADDR_START;
        offset = address * sizeof(uint16_t);//same as     uint8_t offset = address * 2;
        if (offset + num > sizeof(dat_ram.read_write_cal))
            exception_code = 2; /* out of range */
        else
        {
            bytes_ptr = (uint8_t*)&dat_ram.read_write_cal;
            cal_block_changed = TRUE; /* Something changed */
        }
    }
    else
        exception_code = 2; /* Bad address */

    if (exception_code == 0)
    {
        for(i = 0; i < num; i += 2)
        {
            bytes_ptr[offset+i] = buf[i+1];
            bytes_ptr[offset+i+1] = buf[i];
        }
    }
		
    if(input_regs_changed && exception_code==0)
    {
		
    }
		
		/* LM add @2020-05-28 */
	  if ( (cal_block_changed == TRUE) && (exception_code == 0) )
    {
			cal_block_changed = FALSE;
			
			/*--------------------------------*/
		}

    return exception_code;
}

/****************************************************************************
   This Function will read from registers in either RAM. The data to be read
   can be a string of contiguous data
*/
static uint8_t read_data(uint16_t address, uint8_t num, uint8_t* buf)
{
    uint8_t i = 0;
    uint16_t offset;
    uint8_t* bytes_ptr;
    uint8_t  exception_code = 0;

		if (address <= OUT_REGS_ADDR_END) //*Holding Regs 0x1FF
    {
        address = address - OUT_REGS_ADDR_START;
        offset = address * sizeof(uint16_t);

        if (offset + num > sizeof(dat_ram.write))
            exception_code = 2; /* out of range */
        else
        {
            bytes_ptr = &dat_ram.write.bytes[0];
        }
    }

    /* Read/write registers; Block name ModuleInfo */
    else if ( (address >= INFO_REGS_ADDR_START) && (address <= INFO_REGS_ADDR_END) ) //*[512,1021]
    {
        address = address - INFO_REGS_ADDR_START;
        offset = address * sizeof(uint16_t);//same as     uint8_t offset = address * 2;

        if (offset + num > sizeof(dat_ram.read_write_info))
            exception_code = 2; /* out of range */
        else
        {
            //bytes_ptr = (uint8_t*)&dat_ram.read_write_info;
            bytes_ptr = (uint8_t*)&dat_ram.read_write_info.board_type;
        }
    }

    /* Read/Write; Block name Writings */
    else if ( (address >= IN_REGS_ADDR_START) && (address <= IN_REGS_ADDR_END) ) //*[1536,2047]
    {
        address = address - IN_REGS_ADDR_START;
        offset = address * sizeof(uint16_t);//same as     uint8_t offset = address * 2;
        
        if (offset + num > sizeof(dat_ram.read))
            exception_code = 2; /* out of range */
        else
        {
            bytes_ptr = (uint8_t*)&dat_ram.read;
        }
    }

    /* Read/write registers,  Block name Calibrations */
    else if ((address >= CAL_REGS_ADDR_START) && (address <= CAL_REGS_ADDR_END) ) //*[2048,2559]
    {
        address = address - CAL_REGS_ADDR_START;
        offset = address * sizeof(uint16_t);//same as     uint8_t offset = address * 2;
        
        if (offset + num > sizeof(dat_ram.read_write_cal))
            exception_code = 2; /* out of range */
        else
        {
            bytes_ptr = (uint8_t*)&dat_ram.read_write_cal;
        }
    }
    else
        exception_code = 2; /* Bad Starting Address */

    if (exception_code == 0)
    {
        for(i = 0; i  < num; i += 2)
        {
            buf[i] = bytes_ptr[offset + i + 1]; //byte swap
            buf[i+1] = bytes_ptr[offset + i];
        }
    }

    return exception_code;
}

//void database_init()
//{
//	dat_ram.read.EPC_Board.addr_A_setP = 50; //50 / 100 = 0.5
//	dat_ram.read.EPC_Board.addr_A_setI = 2400;
//	dat_ram.read.EPC_Board.addr_A_setD = 400;
//	
//	dat_ram.read.EPC_Board.addr_B_setP = 50; //50 / 100 = 0.5
//	dat_ram.read.EPC_Board.addr_B_setI = 2400;
//	dat_ram.read.EPC_Board.addr_B_setD = 400;
//	
//	dat_ram.read.EPC_Board.addr_C_setP = 50; //50 / 100 = 0.5
//	dat_ram.read.EPC_Board.addr_C_setI = 2400;
//	dat_ram.read.EPC_Board.addr_C_setD = 400;
//	
//	//dat_ram.read.EPC_Board.addr_slope_A = 0.1234;
//	
//}
